/* <unfinished>
   
   jQuery Twitter Plugin
   \Author: Kevin Mesiab
   Date: May 1, 2009
   
   \Version: .01 Beta
   
   Description
   The TwitterApi class simplifies access to the Twitter.com
   REST API.
	
	JSON Response object format:
	
	{
		"success":false,
		"url":"http://search.twitter.com/search.json?q=kmesiab&rpp=10&page=0&geocode=",
		"error":"Invalid Parameter",
		"response":{
			"error":"Invalid Parameter"
		}
	}
	
	{
		"success":true,
		"url":"http://search.twitter.com/trends/current.json",
		"error":null,
		"response":{
			"as_of":1243900524,
			"trends":{
				"2009-06-01 23:55:24":[
					{
						"query":"#musicmonday",
						"name":"#musicmonday"
					},
					{
						"query":"#e3",
						"name":"#e3"
					}
				]
			}
		}
	}
   
   \NOTE: The format of response can be determined by examining
   the Twitter API documentation: http://apiwiki.twitter.com
   
   
   Parameters
   username :  Users Twitter username
   password :  Users Twitter password                           */
function TwitterApi(username, password) {

	this.timeout 		= 25000;  
	this.format 		= "json";
	this.cache			= true;
	this.ifModified		= false;
	this.queue			= true;
	this.method			= "GET";
	this.userAgent 		= "Hummingbird/2.0.55";
	this.referrer		= "http://hummingbird2.com";
	
	this.username = username;
	this.password = password;
	
	this.trends 		= {};
	this.timeline 		= {};
	this.statuses 		= {};
	this.users 			= {};
	this.directmessage 	= {};
	this.friendships 	= {};
	this.socialgraph 	= {};
	this.account 		= {};
	this.favorites 		= {};
	this.notifications 	= {};
	this.blocks 		= {};
	this.help 			= {};
	
	
	var self				= this;
	this.trends.self 		= this;
	this.timeline.self 		= this;
	this.statuses.self 		= this;
	this.users.self			= this;
	this.directmessage.self = this;
	this.friendships.self 	= this;
	this.socialgraph.self 	= this;
	this.account.self 		= this;
	this.favorites.self		= this;
	this.notifications.self = this;
	this.blocks.self 		= this;
	this.help.self 			= this;
	
	
	/********** Twitter Search Functions **********/
	
	//Returns tweets that match a specified search_term.
	// search_term, callback, tweets_per_page, page_num, geo_code, since_id, show_user, progress
	this.search = function( params, complete, progress ) {
		
		self.callApi("search", complete, progress, params );	
	}

	/********** Twitter Trend Functions **********/

	// Returns the current top 10 trending topics on Twitter.  
	// The response includes the time of the request, the name 
	// of each trending topic, and query used on Twitter Search 
	// results page for that topic.
	this.trends.current = function( complete, progress ) {
		self.callApi("trends/current", complete, progress);
	}
	
	//Returns the top 20 trending topics for each hour in 
	// a given day.
	this.trends.daily = function( complete, progress ) {
		self.callApi("trends/daily", complete, progress);
	}
	
	//Returns the top 30 trending topics for each day in 
	// a given week.
	this.trends.weekly = function( complete, progress ) {
		self.callApi("trends/weekly", complete, progress);
	}
	
	/********** Twitter Statuses Functions **********/
	
	//Returns the 20 most recent statuses from non-protected 
	// users who have set a custom  user icon. The public timeline 
	// is cached for 60 seconds so requesting it more often than that 
	// is a waste of resources.
	this.statuses.publicTimeline = function( complete, progress ) {
		self.callApi("statuses/public_timeline", complete, progress);
	}

	// :Auth Required
	// Returns the 20 most recent statuses posted by the authenticating 
	// user and that user's friends. This is the equivalent of 
	// /timeline/home on the Web.
	//
	// @since_id : 
	// @max_id : 
	// @count : 
	// @page : 
	this.statuses.friendsTimeline = function(params, complete, progress) {
		
		self.callApi("statuses/friends_timeline", complete, progress, params, true);
	}
	
	// Returns the 20 most recent mentions (status containing @username) 
	// for the authenticating user.
	//
	// @since_id : 
	// @max_id : 
	// @count : 
	// @page : 
	this.statuses.mentions = function( params, complete, progress ) {
	
		self.callApi("statuses/mentions", complete, progress, params, true);
	}
	
	// Returns the 20 most recent statuses posted from the authenticating 
	// user. It's also possible to request another user's timeline via the 
	// id parameter. This is the equivalent of the Web /<user> page for your 
	// own user, or the profile page for a third party.
	//
	// @since_id : 
	// @max_id : 
	// @count : 
	// @page : 
	this.statuses.userTimeline = function(p, complete, progress) {
		
		self.callApi("statuses/user_timeline", complete, progress, p, true );
	}
	
	// Returns a single status, specified by the id parameter below.  
	// The status's author will be returned inline.
	// 
	// @id : 
	this.statuses.show = function(params, progress, complete) {
	
		if( !params.id ) this.fatal( 'Required parameter id was not supplied' );
		self.callApi("statuses/show", callback, {id : params.id});
	}
	
	// :Auth Required
	// Updates the authenticating user's status.  Requires the status 
	// parameter specified below.  Request must be a POST.  A status update 
	// with text identical to the authenticating user's current status will 
	// be ignored to prevent duplicates.
	//
	// @status
	// @in_reply_to_status_id
	this.statuses.update = function( params, complete, progress ) {
		
		if( !params.status ) {
			this.self.fatal( 'Cannot send an empty tweet. Parameter status undefined' );
			return;
		}
		
		self.callApi(
			"statuses/update", complete, progress, params, true, "POST"
		);
	}
	
	// :Auth Required
	// Destroys the status specified by the required ID parameter.  
	// The authenticating user must be the author of the specified status.
	// 
	// @id : 
	this.statuses.destroy = function(params, complete, progress)  {
		
		if( !params.id ) 
		{
			this.self.fatal( 'Required parameter id was not supplied' );
			return;
		}
		
		self.callApi("statuses/destroy", complete, progress, params, true, "POST");
	
	}

	/********** Twitter Users Functions **********/
	
	//Returns extended information of a given user, specified by ID or screen 
	// name as per the required id parameter.  The author's most recent status 
	// will be returned inline.
	//
	// @user :
	this.users.show = function( params, complete, progress ) {
		
		self.callApi(
			"users/show", complete, progress, params 
		);
	}
	
	// Returns a user's friends, each with current status inline. They are ordered 
	// by the order in which they were added as friends. Defaults to the authenticated 
	// user's friends. It's also possible to request another user's friends list via the id
	//
	// @user : 
	// @page : 
	this.users.friends = function( params, complete, progress ){

		self.callApi(
			"statuses/friends",
			complete, progress,
			params
		);
	}
	
	//Returns the authenticating user's followers, each with current status inline.  
	// They are ordered by the order in which they joined Twitter
	//
	// @user : 
	// @page : 
	this.users.followers = function( params, complete, progress ) {
		
		self.callApi(
			"statuses/followers",
			callback,
			params
		);
		
	}
	
	/********** Direct Message Functions ***************/
	
	// :Auth Required
	// Returns a list of the 20 most recent direct messages sent to the 
	// authenticating user.  The XML and JSON versions include detailed 
	// information about the sending and recipient users.
	//
	// @since_id : 
	// @max_id : 
	// @count : 
	// @page : 
	this.directmessage.messages = function( params, complete, progress ) {
		
		self.callApi("direct_messages", complete, progress, params, true);
	}
	
	// :Auth Required
	// Returns a list of the 20 most recent direct messages sent by the 
	// authenticating user.  The XML and JSON versions include detailed 
	// information about the sending and recipient users.
	//
	// @since_id : 
	// @max_id : 
	// @count : 
	// @page : 
	this.directmessage.sent = function( params, complete, progress ) {
		
		self.callApi("direct_messages/sent", complete, progress, params, true);
	}
	
	// :Auth Required
	// Sends a new direct message to the specified user from the authenticating 
	// user. Requires both the user and text parameters. Request must be a POST. 
	// Returns the sent message in the requested format when successful.
	//
	// @user : 
	// @text : 
	this.directmessage.send = function( params, complete, progress ) {
		 
		if( !user ) throw 'User required to send direct message';
		if( !text ) throw 'Text required to send direct message';
		
		self.callApi("direct_messages/new", complete, progress, params, true, "POST");
	}
	
	// :Auth Required
	// Destroys the direct message specified in the required ID parameter.  
	// The authenticating user must be the recipient of the specified direct message.
	this.directmessage.destroy = function( callback, id ) {
		
		if( !id ) this.self.fatal('Id required to delete a direct message');
		self.callApi("direct_messages/destroy", callback, {id:id}, true, "POST");
	}
	
	/************* Friendship Methods ********************/
	
	// :Auth Required
	// Allows the authenticating users to follow the user specified in the 
	// ID parameter.  Returns the befriended user in the requested format when 
	// successful.  Returns a string describing the failure condition when unsuccessful.
	//
	// @user : the screen name or user id to follow
	this.friendships.create = function(params, complete, progress){
	
		self.callApi("friendships/create", complete, progress, params, true, "POST");
	}
	
	// :Auth Required
	// Allows the authenticating users to unfollow the user 
	// specified in the ID parameter.  Returns the unfollowed user in 
	// the requested format when successful.  Returns a string describing the 
	// failure condition when unsuccessful.
	//
	// @user : the screen name or user id to compare
	this.friendships.destroy = function(params, complete, progress){
		self.callApi("friendships/destroy", complete, progress, params, true, "POST");
	}
	
	// Tests for the existence of friendship between two users. Will return 
	// true if user_a follows user_b, otherwise will return false.
	//
	// NOTE: If you specify true for authentication here, you can test friendships
	// against protected users whom are friends with the authenticated user.
	//
	// @usera : the first screen name 
	// @userb : the second screen name
	this.friendships.exists = function(params, complete, progress){
	
		if( !usera ) throw 'userA is required to determine if a friendship exists';
		if( !userb ) this.self.fata('userB is required to determine if a friendship exists');
	
		self.callApi("friendships/exists", progress, complete, params);
	}
	
	/************* Social Graph Functions ******************/
	
	// Returns an array of numeric IDs for every 
	// user the specified user is following.
	//
	// @user : user_id or screen_name
	// @page : (optional) the page to begin on
	this.socialgraph.friendIds = function( params, complete, progress ) {
		self.callApi(
			"friends/ids",
			complete, progress,
			params
		);
	}
	 
	// Returns an array of numeric IDs for every 
	// user following the specified user.
	//
	// @user : user_id or screen_name
	// @page : (optional) the page to begin on
	this.socialgraph.followerIds = function( params, complete, progress ) {
			
		self.callApi(
			"followers/ids",
			complete, progress,
			params
		);
	}
	
	
	/************ Account Functions *******************/
	
	// :Auth Required
	// Returns an HTTP 200 OK response code and a representation of the 
	// requesting user if authentication was successful; returns a 401 
	// status code and an error message if not.  Use this method to test
	// if supplied user credentials are valid.
	this.account.verifyCredentials = function( complete, progress ) {
	
		this.self.callApi(
		"account/verify_credentials",
		complete, progress, null, true
		);
		
	}
	
	
	//Returns the remaining number of API requests available to the requesting 
	// user before the API limit is reached for the current hour. Calls to 
	// rate_limit_status do not count against the rate limit.  If authentication 
	// credentials are provided, the rate limit status for the authenticating user 
	// is returned.  Otherwise, the rate limit status for the requester's IP address 
	// is returned. Learn more about the REST API rate limiting.
	this.account.rateLimitStatus = function( complete, progress ) {
		
		var auth = (this.self.username && this.password);
		
		self.callApi(
			"account/rate_limit_status",
			complete, progress, null, auth
		);
		
	}
	
	// Ends the session of the authenticating user, returning a null cookie.  
	// Use this method to sign users out of client-facing applications like widgets.
	this.account.endSession = function(complete, progress) {
		
		self.callApi(
			"account/end_session",
			complete, progress, 
			null, true, "POST"
		);
		
	}
	
	
	this.account.updateDeliveryDevice = function(){}
	this.account.updateProfileColors = function(){}
	this.account.updateProfileImage = function(){}
	this.account.updateProfileBackgroundImage = function(){}
	this.account.updateProfile = function(){}
	
	/************ Favorite Functions ****************************/
	
	
	/***************** Helper Functions *************************/
	// Calls the twitter api for the args 
	// specified.  
	//
	// The results of the function call will be returned
	// to the callback, and passed the results as json
	this.callApi = function( type, complete, progress, params, auth_required, method ) {	
			
		// Check for params
		if( !type ) fatal(
			'The Twitter API was called with no api function name'
		);
	
		method = (!method) ? "GET" : method;
		auth_required = (!auth_required) ? false : auth_required;
		
		// Check for login
		if( auth_required && (!username || !password) )
			throw 'Must call $.twitter.login() first';
		
		var url = this.getApiUrl(type);
		
		$.web.ajax({
			url: url,
			data: params,
			method: method,
			responsetype: 'json',
			authenticate: auth_required,
			username: username,
			password: password,
			complete: complete,
			progress: progress
		})
	
	}
	// Creates the full valid Twitter API url by 
	// figuring out the base url, appending the method
	// name and type 
	// 
	// Returns a derived API url
	this.getApiUrl= function(type) {	
		
		var p = "http://";
		var b = "twitter.com/";
		var s = "search.twitter.com/";
		
		// Set the appropriate API base url (search vs REST)
		var h = (type.match('trends') || type.match('search')) ? s : b;
		
		// Return the full url
		return p+h+type+'.'+this.format;
	}

}

